package com.ceair.service.impl;

import cn.hutool.core.date.DateUtil;
import com.ceair.api.SystemFeignClient;
import com.ceair.config.CustomProcessDiagramGenerator;
import com.ceair.entity.model.UserInfo;
import com.ceair.entity.request.PageReq;
import com.ceair.entity.request.PickupMyTaskReq;
import com.ceair.entity.request.QueryCirculationRecordsReq;
import com.ceair.entity.request.RevertMyTaskReq;
import com.ceair.entity.result.Result;
import com.ceair.entity.vo.*;
import com.ceair.exception.BusinessException;
import com.ceair.service.IMayTaskService;
import com.ceair.util.UserInfoUtils;
import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangbaohai
 * @ClassName MayTaskServiceImpl
 * @description: 我的任务信息接口实现
 * @date 2025年05月01日
 * @version: 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MayTaskServiceImpl implements IMayTaskService {

    private final RepositoryService repositoryService;
    private final SystemFeignClient systemFeignClient;
    private final TaskService taskService;
    private final HistoryService historyService;
    private final RuntimeService runtimeService;
    private final ProcessEngine processEngine;

    // 用户工具
    private final UserInfoUtils userInfoUtils;

    /**
     * 查询当前用户代办的任务列表
     *
     * @param pageReq 分页请求对象，包含当前页码和每页大小
     * @return 返回包含任务列表和总任务数的TaskListInfoVO对象
     * @throws IllegalArgumentException 如果分页信息为空，则抛出此异常
     * @throws BusinessException        如果用户未登录或查询角色ID失败，则抛出此异常
     */
    @Override
    public TaskListInfoVO myTodoTask(PageReq pageReq) {
        try {
            // 初始化
            TaskListInfoVO taskListInfoVO = new TaskListInfoVO();

            // 分页信息判空
            if (pageReq == null) {
                log.error("查询我的待办任务失败，原因：分页信息不能为空");
                throw new IllegalArgumentException("查询我的待办任务失败，原因：分页信息不能为空");
            }

            // 获取分页信息，如果不存在默认查询第一页，每页10条数据
            long current = (Objects.nonNull(pageReq.getCurrent()) && pageReq.getCurrent() > 0) ? pageReq.getCurrent() :
                    1L;
            long size = (Objects.nonNull(pageReq.getSize()) && pageReq.getSize() > 0) ? pageReq.getSize() : 10L;

            // 获取当前用户
            UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();
            if (userInfo == null) {
                log.error("查询我的待办任务失败，原因：用户未登录");
                throw new BusinessException("查询我的待办任务失败，原因：用户未登录");
            }

            // 缓存用户ID字符串形式，避免重复调用
            String userIdStr = userInfo.getId().toString();
            String account = userInfo.getAccount();

            // 通过 feign 接口获取当前用的角色ID清单
            Collection<String> roleIds;
            Result<List<Long>> roleResult = systemFeignClient.queryRoleIdsByUserId(userInfo.getId());
            if (roleResult.getCode() != 200 || roleResult.getData() == null) {
                log.warn("查询我的待办任务失败，原因：{}", roleResult.getMessage());
                roleIds = Collections.emptyList();
            } else {
                roleIds = roleResult.getData().stream().map(String::valueOf).collect(Collectors.toList());
            }

            // 设置查询工具(需要查询激活的，办理人/候选人/候选组是我的任务)
            TaskQuery taskQuery = taskService.createTaskQuery()
                    .active()
                    .or()
                    .taskAssignee(userIdStr)
                    .taskCandidateUser(userIdStr);

            if (!roleIds.isEmpty()) {
                taskQuery.taskCandidateGroupIn(roleIds);
            }

            taskQuery.endOr()
                    .orderByTaskCreateTime()
                    .desc();

            // 查询工具设置分页属性并且实行查询动作
            List<Task> tasks = taskQuery.listPage((int) ((current - 1) * size), (int) (current * size));

            // 记录待办任务数量
            long count = taskQuery.count();

            // 初始化结果数据list
            List<TaskVO> taskVOS = new ArrayList<>();

            // 翻译任务数据
            tasks.stream()
                    .filter(Objects::nonNull)
                    .forEach(task -> {
                        TaskVO taskVO = new TaskVO();
                        taskVO.setTaskId(task.getId());
                        taskVO.setExecutionId(task.getExecutionId());
                        taskVO.setTaskName(task.getName());
                        taskVO.setProcDefId(task.getProcessDefinitionId());
                        taskVO.setTaskDefKey(task.getTaskDefinitionKey());
                        taskVO.setAssigneeId(task.getAssignee());
                        taskVO.setAssigneeName(StringUtils.isBlank(task.getAssignee()) ? "" : account);

                        // 查询确认 流程定义数据
                        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                                .processDefinitionId(task.getProcessDefinitionId())
                                .singleResult();
                        if (processDefinition != null) {
                            taskVO.setProcDefName(processDefinition.getName());
                            taskVO.setProcDefKey(processDefinition.getKey());
                            taskVO.setProcInsId(task.getProcessInstanceId());
                        }

                        // 查询确认 流程发起人
                        HistoricProcessInstance historicProcessInstance =
                                historyService.createHistoricProcessInstanceQuery()
                                        .processInstanceId(task.getProcessInstanceId()).singleResult();
                        if (historicProcessInstance != null
                                && StringUtils.isNotBlank(historicProcessInstance.getStartUserId())) {
                            // 确认 流程发起人
                            String startUserId = historicProcessInstance.getStartUserId();
                            taskVO.setStartUserId(startUserId);
                            // 确认流程发起时间
                            taskVO.setStartTime(DateUtil.toLocalDateTime(historicProcessInstance.getStartTime()));
                            // 通过feign接口使用用户ID获取用户信息
                            Result<Oauth2BasicUserVO> userResult =
                                    systemFeignClient.queryUserById(Long.valueOf(startUserId));
                            if (userResult.getCode() == 200 && userResult.getData() != null) {
                                taskVO.setStartUserName(userResult.getData().getName());
                            }
                        }

                        // 确认任务对于当前办理人是需要办理还是拾取还是归还
                        taskVO.setStatus(judgeStatus(task.getProcessDefinitionId(), task.getTaskDefinitionKey(),
                                task.getAssignee()));

                        // 集成封装
                        taskVOS.add(taskVO);
                    });

            // 返回结果
            taskListInfoVO.setTaskCount(count);
            taskListInfoVO.setTaskList(taskVOS);
            return taskListInfoVO;
        } catch (IllegalArgumentException e) {
            log.error("查询我的待办任务失败，原因：参数错误", e);
            throw new BusinessException("查询我的待办任务失败，原因：参数错误", e);
        } catch (BusinessException e) {
            log.error("查询我的待办任务失败，原因：业务异常", e);
            throw new BusinessException("查询我的待办任务失败，原因：业务异常", e);
        } catch (Exception e) {
            log.error("查询我的待办任务失败，原因：未知异常", e);
            throw new BusinessException("查询我的待办任务失败，原因：未知异常", e);
        }
    }

    /**
     * 拾取我的待办任务
     * <p>
     * 此方法允许当前登录用户拾取一个待办任务通过提供任务ID和当前用户信息，
     * 系统将该任务分配给当前用户
     *
     * @param pickupMyTaskReq 包含任务ID的请求对象如果请求对象或任务ID为空，
     *                        将抛出IllegalArgumentException异常
     * @return 任务拾取成功返回true，否则抛出异常
     * @throws BusinessException        如果用户未登录或任务拾取过程中发生业务异常
     * @throws IllegalArgumentException 如果输入参数不合法，如任务ID为空
     */
    @Override
    public Boolean pickupMyTask(PickupMyTaskReq pickupMyTaskReq) {
        try {
            // 参数判空
            if (pickupMyTaskReq == null || StringUtils.isBlank(pickupMyTaskReq.getTaskId())) {
                log.error("任务拾取失败：非法的任务ID");
                throw new IllegalArgumentException("任务拾取失败：非法的任务ID");
            }

            // 获取当前登录用户信息
            UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();
            if (userInfo == null) {
                log.error("拾取我的待办任务失败，原因：用户未登录");
                throw new BusinessException("拾取我的待办任务失败，原因：用户未登录");
            }

            // 通过 taskService 拾取任务
            taskService.claim(pickupMyTaskReq.getTaskId(), String.valueOf(userInfo.getId()));

            return true;
        } catch (IllegalArgumentException e) {
            log.error("任务拾取失败，原因：参数错误", e);
            throw new BusinessException("任务拾取失败，原因：参数错误", e);
        } catch (BusinessException e) {
            log.error("任务拾取失败，原因：业务异常", e);
            throw new BusinessException("任务拾取失败，原因：业务异常", e);
        } catch (Exception e) {
            log.error("任务拾取失败，原因：未知异常", e);
            throw new BusinessException("任务拾取失败，原因：未知异常", e);
        }
    }

    /**
     * 归还我的任务
     * <p>
     * 此方法接收一个 RevertMyTaskReq 对象作为参数，该对象包含任务ID，
     * 方法使用此ID通过 taskService 将任务归还到未分配状态
     *
     * @param revertMyTaskReq 包含任务ID的请求对象，用于指定要归还的任务
     * @return 如果任务成功归还，返回 true；否则抛出异常
     * @throws BusinessException 当参数非法或业务逻辑出现问题时抛出此异常
     */
    @Override
    public Boolean revertMyTask(RevertMyTaskReq revertMyTaskReq) {
        try {
            // 参数判空
            if (revertMyTaskReq == null || StringUtils.isBlank(revertMyTaskReq.getTaskId())) {
                log.error("归还任务失败：非法的任务ID");
                throw new IllegalArgumentException("归还任务失败：非法的任务ID");
            }

            // 通过 taskService 归还任务
            taskService.unclaim(revertMyTaskReq.getTaskId());

            return true;
        } catch (IllegalArgumentException e) {
            log.error("归还任务失败，原因：参数错误", e);
            throw new BusinessException("归还任务失败，原因：参数错误", e);
        } catch (BusinessException e) {
            log.error("归还任务失败，原因：业务异常", e);
            throw new BusinessException("归还任务失败，原因：业务异常", e);
        } catch (Exception e) {
            log.error("归还任务失败，原因：未知异常", e);
            throw new BusinessException("归还任务失败，原因：未知异常", e);
        }
    }

    /**
     * 查询指定流程实例的任务图片，并返回其 Base64 编码字符串。
     * <p>
     * 此方法用于根据传入的流程实例 ID 获取对应的流程图，
     * 并高亮显示该流程中已经执行过的节点和连线，
     * 最终将生成的图片转换为 Base64 编码字符串以便在前端展示。
     * </p>
     *
     * @param processInstanceId 流程实例的唯一标识符，不能为空或空白字符串
     * @return 返回流程图的 Base64 编码字符串表示
     * @throws IllegalArgumentException 如果传入的流程实例ID为空或无效参数
     * @throws BusinessException        如果流程实例不存在或业务处理过程中发生异常
     * @throws Exception                其他未预期的异常
     */
    @Override
    public String queryTaskImage(String processInstanceId) {
        // 初始化流程定义ID、高亮节点列表和连线列表、Base64 图片结果
        String processDefinitionId;
        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        String base64Image = "";

        try {
            // 参数校验：判断流程实例ID是否为空
            if (StringUtils.isBlank(processInstanceId)) {
                log.error("查询任务图片失败：非法的流程实例ID");
                throw new IllegalArgumentException("查询任务图片失败：非法的流程实例ID");
            }

            // 尝试获取当前运行中的流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();

            // 判断流程是否结束
            if (Objects.isNull(processInstance)) {
                // 流程已结束，尝试从历史记录中获取流程定义ID
                HistoricProcessInstance historicProcessInstance =
                        historyService.createHistoricProcessInstanceQuery()
                                .processInstanceId(processInstanceId).singleResult();

                if (Objects.isNull(historicProcessInstance)) {
                    // 历史记录也不存在，说明流程数据不合法
                    log.error("查询任务图片失败：流程实例结束节点不存在");
                    throw new BusinessException("查询任务图片失败：流程实例结束节点不存在");
                }

                // 使用历史流程实例获取流程定义ID
                processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            } else {
                // 流程仍在运行，使用运行时实例获取流程定义ID
                processDefinitionId = processInstance.getProcessDefinitionId();
            }

            // 查询所有已执行的历史活动节点（按开始时间升序排列）
            List<HistoricActivityInstance> activityInstances = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceStartTime().asc()
                    .list();

            // 遍历历史活动实例，分类为高亮节点和高亮连线
            for (HistoricActivityInstance instance : activityInstances) {
                if ("sequenceFlow".equals(instance.getActivityType())) {
                    // 当前为序列流（连接线），加入高亮连线列表
                    highLightedFlows.add(instance.getActivityId());
                } else {
                    // 当前为节点类型（如用户任务、服务任务等），加入高亮节点列表
                    highLightedNodes.add(instance.getActivityId());
                }
            }

            // 获取 BpmnModel 对象，用于后续绘制流程图
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

            // 获取流程引擎配置信息，包括字体、类加载器等
            ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();

            // 创建自定义的流程图生成器，支持高亮显示
            ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();

            // 调用 generateDiagram 方法生成 PNG 格式的流程图图像
            try (InputStream inputStream = diagramGenerator.generateDiagram(
                    bpmnModel, "png", highLightedNodes, highLightedFlows,
                    processEngineConfiguration.getActivityFontName(),
                    processEngineConfiguration.getLabelFontName(),
                    processEngineConfiguration.getAnnotationFontName(),
                    processEngineConfiguration.getClassLoader(), 1.0, true)) {

                // 将输入流中的字节全部读取并编码为 Base64 字符串
                // 注意：readAllBytes() 在大文件下可能占用较多内存，如有需要可改用缓冲方式读取
                base64Image = Base64.getEncoder().encodeToString(inputStream.readAllBytes());
            } catch (IOException e) {
                // IO 异常处理，如读取流程图失败
                log.error("查询任务图片失败：IO异常", e);
                throw new BusinessException("查询任务图片失败：IO异常", e);
            }

            // 返回 Base64 编码的图片字符串
            return base64Image;

        } catch (IllegalArgumentException e) {
            // 捕获参数非法异常并封装为业务异常重新抛出
            log.error("查询任务图片失败：非法参数异常", e);
            throw new BusinessException("查询任务图片失败：非法参数异常", e);
        } catch (BusinessException e) {
            // 捕获业务逻辑异常并重新抛出，避免重复日志输出
            log.error("查询任务图片失败：业务异常", e);
            throw new BusinessException("查询任务图片失败：业务异常", e);
        } catch (Exception e) {
            // 捕获未知异常并封装为业务异常抛出
            log.error("查询任务图片失败：未知异常", e);
            throw new BusinessException("查询任务图片失败：未知异常", e);
        }
    }

    /**
     * 查询当前用户启动的任务列表
     *
     * @param pageReq 分页请求对象，包含当前页码和每页大小
     * @return 返回一个包含任务列表和总数的VO对象
     * @throws IllegalArgumentException 如果分页信息为空，则抛出此异常
     * @throws BusinessException        如果用户未登录或其他业务逻辑异常，则抛出此异常
     */
    @Override
    public MyStartTaskListInfoVO queryMyStartTaskList(PageReq pageReq) {
        try {
            // 初始化
            MyStartTaskListInfoVO myStartTaskListInfoVO = new MyStartTaskListInfoVO();

            // 分页信息判空
            if (pageReq == null) {
                log.error("查询我的已启动任务列表失败，原因：分页信息不能为空");
                throw new IllegalArgumentException("查询我的已启动任务列表失败，原因：分页信息不能为空");
            }

            // 获取分页信息，如果不存在默认查询第一页，每页10条数据
            long current = (Objects.nonNull(pageReq.getCurrent()) && pageReq.getCurrent() > 0) ? pageReq.getCurrent() :
                    1L;
            long size = (Objects.nonNull(pageReq.getSize()) && pageReq.getSize() > 0) ? pageReq.getSize() : 10L;

            // 获取当前用户
            UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();
            if (userInfo == null) {
                log.error("查询我的已启动任务列表失败，原因：用户未登录");
                throw new BusinessException("查询我的已启动任务列表失败，原因：用户未登录");
            }

            // 根据当前登录用户查询所有发起的流程
            HistoricProcessInstanceQuery historicProcessInstanceQuery =
                    historyService.createHistoricProcessInstanceQuery()
                            .startedBy(userInfo.getId().toString())
                            .orderByProcessInstanceStartTime()
                            .desc();

            // 分页查询
            List<HistoricProcessInstance> historicProcessInstanceList = historicProcessInstanceQuery
                    .listPage((int) (current - 1) * (int) size, (int) size);

            // 记录数据总数
            long count = historicProcessInstanceQuery.count();

            // 遍历 historicProcessInstanceList
            List<MyStartTaskVO> myStartTaskList = new ArrayList<>();
            historicProcessInstanceList.stream().filter(Objects::nonNull).forEach(historicProcessInstance -> {
                // 初始化
                MyStartTaskVO myStartTaskVO = new MyStartTaskVO();

                // 转换实例获取impl
                HistoricProcessInstanceEntityImpl historicProcessInstanceEntity =
                        (HistoricProcessInstanceEntityImpl) historicProcessInstance;

                // 根据流程定义ID获取流程定义信息
                ProcessDefinition processDefinition =
                        repositoryService.createProcessDefinitionQuery()
                                .processDefinitionId(historicProcessInstanceEntity.getProcessDefinitionId())
                                .singleResult();

                // 收集我的发起任务信息
                myStartTaskVO.setProcessDefinitionId(processDefinition.getId());
                myStartTaskVO.setProcessDefinitionName(processDefinition.getName());
                myStartTaskVO.setProcessInstanceId(historicProcessInstanceEntity.getId());
                myStartTaskVO.setStartUserName(userInfo.getAccount());
                myStartTaskVO.setStartTime(DateUtil.toLocalDateTime(historicProcessInstanceEntity.getStartTime()));
                myStartTaskVO.setEndTime(DateUtil.toLocalDateTime(historicProcessInstanceEntity.getEndTime()));

                myStartTaskList.add(myStartTaskVO);
            });

            // 收集结果数据
            myStartTaskListInfoVO.setMyStartTaskList(myStartTaskList);
            myStartTaskListInfoVO.setMyStartTaskCount(count);
            return myStartTaskListInfoVO;
        } catch (IllegalArgumentException e) {
            // 捕获参数非法异常并封装为业务异常重新抛出
            log.error("查询我的已启动任务列表失败：非法参数异常", e);
            throw new BusinessException("查询我的已启动任务列表失败：非法参数异常", e);
        } catch (BusinessException e) {
            // 捕获业务逻辑异常并重新抛出，避免重复日志输出
            log.error("查询我的已启动任务列表失败：业务异常", e);
            throw new BusinessException("查询我的已启动任务列表失败：业务异常", e);
        } catch (Exception e) {
            // 捕获未知异常并封装为业务异常抛出
            log.error("查询我的已启动任务列表失败：未知异常", e);
            throw new BusinessException("查询我的已启动任务列表失败：未知异常", e);
        }
    }

    /**
     * 查询流程实例的流转记录。
     *
     * @param queryCirculationRecordsReq 请求参数，包含流程实例ID等信息。
     * @return 返回流转记录的VO列表，每个VO表示一个任务的流转信息。
     * @throws BusinessException        如果参数为空、查询用户信息失败或发生业务异常。
     * @throws IllegalArgumentException 如果请求参数非法。
     */
    @Override
    public List<CirculationRecordsVO> queryCirculationRecords(QueryCirculationRecordsReq queryCirculationRecordsReq) {
        try {
            // 初始化结果列表
            List<CirculationRecordsVO> circulationRecordsVOList = new ArrayList<>();

            // 参数校验：判断请求对象是否为空或者流程实例ID是否为空
            if (queryCirculationRecordsReq == null || StringUtils.isBlank(queryCirculationRecordsReq.getProcessInstanceId())) {
                log.error("查询流转记录失败，原因：参数为空");
                throw new IllegalArgumentException("查询流转记录失败，原因：参数为空");
            }

            // 根据流程实例ID查询历史任务，并按创建时间升序排列
            List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceId(queryCirculationRecordsReq.getProcessInstanceId())
                    .orderByTaskCreateTime()
                    .asc()
                    .list();

            // 遍历历史任务列表并构建流转记录
            historicTaskInstanceList.stream().filter(Objects::nonNull).forEach(historicTaskInstance -> {
                CirculationRecordsVO circulationRecordsVO = new CirculationRecordsVO();
                circulationRecordsVO.setTaskId(historicTaskInstance.getId());
                circulationRecordsVO.setProcessInstanceId(historicTaskInstance.getProcessInstanceId());
                circulationRecordsVO.setTaskName(historicTaskInstance.getName());
                circulationRecordsVO.setAssignee(historicTaskInstance.getAssignee());
                circulationRecordsVO.setStartTime(DateUtil.toLocalDateTime(historicTaskInstance.getStartTime()));

                // 通过Feign客户端根据assignee查询用户信息
                Result<Oauth2BasicUserVO> userResult =
                        systemFeignClient.queryUserById(Long.valueOf(historicTaskInstance.getAssignee()));
                if (userResult.getCode() == 200 && userResult.getData() != null) {
                    circulationRecordsVO.setAssigneeName(userResult.getData().getName());
                }

                // 判断任务是否完成（是否有结束时间）
                if (historicTaskInstance.getEndTime() == null) {
                    // 没有结束时间说明还在等待审批，设置红色标记
                    circulationRecordsVO.setSize("large");
                    circulationRecordsVO.setType("primary");
                    circulationRecordsVO.setColor("#F56C6C");
                } else {
                    // 有结束时间说明当前节点审批完成，设置绿色标记
                    circulationRecordsVO.setSize("normal");
                    circulationRecordsVO.setType("success");
                    circulationRecordsVO.setColor("#67C23A");
                }

                // 收集流转记录到结果列表中
                circulationRecordsVOList.add(circulationRecordsVO);
            });

            // 返回最终的结果列表
            return circulationRecordsVOList;
        } catch (IllegalArgumentException e) {
            // 捕获参数非法异常并封装为业务异常重新抛出
            log.error("查询流转记录失败：非法参数异常", e);
            throw new BusinessException("查询流转记录失败：非法参数异常", e);
        } catch (BusinessException e) {
            // 捕获业务逻辑异常并重新抛出，避免重复日志输出
            log.error("查询流转记录失败：业务异常", e);
            throw new BusinessException("查询流转记录失败：业务异常", e);
        } catch (Exception e) {
            // 捕获未知异常并封装为业务异常抛出
            log.error("查询流转记录失败：未知异常", e);
            throw new BusinessException("查询流转记录失败：未知异常", e);
        }
    }

    /**
     * 查询当前用户完成的任务列表
     *
     * @param pageReq 分页请求对象，包含当前页码和每页大小
     * @return 返回一个包含用户完成任务列表和总数的VO对象
     * @throws IllegalArgumentException 如果分页请求参数为空，则抛出此异常
     * @throws BusinessException        如果用户未登录或查询过程中发生业务异常，则抛出此异常
     */
    @Override
    public MyCompleteTaskListInfoVO queryMyCompleteTaskList(PageReq pageReq) {
        try {
            // 初始化
            MyCompleteTaskListInfoVO myCompleteTaskListInfoVO = new MyCompleteTaskListInfoVO();

            // 分页信息判空
            if (pageReq == null) {
                log.error("查询我的已办任务列表失败，原因：分页信息不能为空");
                throw new IllegalArgumentException("查询我的已办任务列表失败，原因：分页信息不能为空");
            }

            // 获取已办分页信息，如果不存在默认查询第一页，每页10条数据
            long current = (Objects.nonNull(pageReq.getCurrent()) && pageReq.getCurrent() > 0) ? pageReq.getCurrent() :
                    1L;
            long size = (Objects.nonNull(pageReq.getSize()) && pageReq.getSize() > 0) ? pageReq.getSize() : 10L;

            // 获取当前用户
            UserInfo userInfo = userInfoUtils.getUserInfoFromAuthentication();
            if (userInfo == null) {
                log.error("查询我的已办任务列表失败，原因：用户未登录");
                throw new BusinessException("查询我的已办任务列表失败，原因：用户未登录");
            }

            // 根据用户ID查询所有我办理的任务
            List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                    .taskAssignee(userInfo.getId().toString()).list();

            // 收集所有我办理的任务的实例ID
            Set<String> processInstanceIds =
                    historicTaskInstances.stream().map(HistoricTaskInstance::getProcessInstanceId)
                            .collect(Collectors.toSet());

            // 根据已收集到的所有实例ID查询所有已完成的任务
            HistoricProcessInstanceQuery historicProcessInstanceQuery =
                    historyService.createHistoricProcessInstanceQuery()
                            .processInstanceIds(processInstanceIds).orderByProcessInstanceStartTime().desc();

            // 分页查询已完成的任务
            List<HistoricProcessInstance> historicProcessInstances =
                    historicProcessInstanceQuery.listPage((int) (current - 1) * (int) size, (int) size);

            // 记录数据总数
            long count = historicProcessInstanceQuery.count();

            // 遍历 historicProcessInstances
            List<MyCompleteTaskVO> myCompleteTaskList = new ArrayList<>();
            historicProcessInstances.stream().filter(Objects::nonNull).forEach(historicProcessInstance -> {
                // 初始化
                MyCompleteTaskVO myCompleteTaskVO = new MyCompleteTaskVO();

                // 转换实例获取impl
                HistoricProcessInstanceEntityImpl historicProcessInstanceEntity =
                        (HistoricProcessInstanceEntityImpl) historicProcessInstance;

                // 根据流程定义ID获取流程定义信息
                ProcessDefinition processDefinition =
                        processEngine.getRepositoryService().createProcessDefinitionQuery()
                                .processDefinitionId(historicProcessInstanceEntity.getProcessDefinitionId())
                                .singleResult();

                // 根据 流程定义的的发起人ID查询名称
                if (StringUtils.isNotBlank(historicProcessInstance.getStartUserId())) {
                    // 通过feign接口使用用户ID获取用户信息
                    Result<Oauth2BasicUserVO> userResult =
                            systemFeignClient.queryUserById(Long.valueOf(historicProcessInstance.getStartUserId()));
                    if (userResult.getCode() == 200 && userResult.getData() != null) {
                        // 设置发起人名称以及开始和结束时间
                        myCompleteTaskVO.setStartUserName(userResult.getData().getName());
                        myCompleteTaskVO.setStartTime(DateUtil.toLocalDateTime(historicProcessInstance.getStartTime()));
                        myCompleteTaskVO.setEndTime(DateUtil.toLocalDateTime(historicProcessInstance.getEndTime()));
                    }
                }

                // 设置我的已办任务信息
                myCompleteTaskVO.setProcessDefinitionId(processDefinition.getId());
                myCompleteTaskVO.setProcessDefinitionName(processDefinition.getName());
                myCompleteTaskVO.setProcessInstanceId(historicProcessInstanceEntity.getId());

                // 收集我的已办任务信息
                myCompleteTaskList.add(myCompleteTaskVO);
            });

            // 设置返回信息
            myCompleteTaskListInfoVO.setMyCompleteTaskList(myCompleteTaskList);
            myCompleteTaskListInfoVO.setMyCompleteTaskCount(count);

            return myCompleteTaskListInfoVO;
        } catch (IllegalArgumentException e) {
            log.error("查询我的已办任务列表失败，原因：参数错误", e);
            throw new BusinessException("查询我的已办任务列表失败，原因：参数错误", e);
        } catch (BusinessException e) {
            log.error("查询我的已办任务列表失败，原因：业务异常", e);
            throw new BusinessException("查询我的已办任务列表失败，原因：业务异常", e);
        } catch (Exception e) {
            log.error("查询我的已办任务列表失败，原因：未知异常", e);
            throw new BusinessException("查询我的已办任务列表失败，原因：未知异常", e);
        }
    }


    /**
     * 根据流程定义ID、任务定义键和指定的办理人判断任务状态
     *
     * @param processDefinitionId 流程定义ID，用于识别特定的业务流程
     * @param taskDefinitionKey   任务定义键，用于在流程中定位特定的任务
     * @param assignee            指定的办理人，用于判断任务的当前状态
     * @return 返回任务的状态代码：0-审批，1-拾取，2-审批或归还；如果无法判断状态，则返回null
     */
    private Integer judgeStatus(String processDefinitionId, String taskDefinitionKey, String assignee) {
        // 参数校验
        if (StringUtils.isBlank(processDefinitionId) || StringUtils.isBlank(taskDefinitionKey)) {
            return null;
        }

        try {
            // 获取 BpmnModel 对象
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
            Process mainProcess = bpmnModel != null ? bpmnModel.getMainProcess() : null;

            if (mainProcess == null) {
                return null;
            }

            Collection<FlowElement> flowElements = mainProcess.getFlowElements();

            // 查找目标用户任务
            return flowElements.stream()
                    .filter(Objects::nonNull)
                    .filter(flowElement -> flowElement instanceof UserTask)
                    .map(flowElement -> (UserTask) flowElement)
                    .filter(userTask -> taskDefinitionKey.equals(userTask.getId()))
                    .findFirst()
                    .map(userTask -> {
                        if (!StringUtils.isBlank(userTask.getAssignee())) {
                            // 流程节点指定办理人：审批状态
                            return 0; // 审批
                        } else {
                            if (StringUtils.isBlank(assignee)) {
                                // 未指定实际办理人：拾取状态
                                return 1; // 拾取
                            } else {
                                // 已有实际办理人：审批/归还状态
                                return 2; // 审批或归还
                            }
                        }
                    })
                    .orElse(null);

        } catch (Exception e) {
            log.error("判断流程状态失败，具体原因为: {}", e.getMessage(), e);
            return null;
        }
    }

}
